home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
admin
/
linuxcon.000
/
linuxcon
/
linuxconf-1.6
/
dialog
/
cmdsock.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-05-12
|
11KB
|
511 lines
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/protocols.h>
#include <limits.h>
#include "cmdsock.h"
#if 1
#define DBGALL 1
void logevent(const char *fmt, ...)
{
va_list list;
va_start (list,fmt);
char buf[300];
sprintf (buf,fmt,list);
syslog (LOG_ERR,buf);
va_end (list);
}
void logdebug (int level, const char *fmt, ...)
{
va_list list;
va_start (list,fmt);
char buf[300];
int len = sprintf (buf,"Debug level %d : ",level);
vsprintf (buf+len,fmt,list);
syslog (LOG_DEBUG,buf);
va_end (list);
}
#endif
/*
Obtient le port de communication via /etc/service
*/
static int cmdsock_getport(const char *service)
{
int ret = -1;
struct servent *serv = getservbyname (service,"tcp");
if (serv == NULL){
logevent ("Pas de service %s dans /etc/service",service);
}else{
ret = ntohs(serv->s_port);
}
return ret;
}
PRIVATE void CMDSOCK::init(int port)
{
listen_handle = -1;
if (port != -1){
/* Il faut creer le socket original pour la connection */
char myhost[PATH_MAX];
gethostname (myhost, sizeof(myhost));
struct hostent *h = (struct hostent *) gethostbyname(myhost);
struct sockaddr_in sin;
sin.sin_family = h->h_addrtype;
logdebug (DBGALL,"Nom du serveur %s %d %d %d.%d.%d.%d\n"
,myhost,h->h_addrtype
,h->h_length
,(unsigned char)h->h_addr[0]
,(unsigned char)h->h_addr[1]
,(unsigned char)h->h_addr[2]
,(unsigned char)h->h_addr[3]);
memcpy (&sin.sin_addr,h->h_addr, h->h_length);
sin.sin_port = htons(port);
int i;
for (i=0; i<5; i++){
listen_handle = socket (AF_INET, SOCK_STREAM, 0);
sin.sin_addr.s_addr = INADDR_ANY;
if (listen_handle == -1){
logdebug (DBGALL,"listen_handle %d(%s)\n"
,errno
,strerror(errno));
}else if (bind (listen_handle,(struct sockaddr *) &sin, sizeof (sin)) == -1){
logdebug (DBGALL,"bind %d(%s)\n",errno
,strerror(errno));
}else if (::listen (listen_handle,5) == -1){
logdebug (DBGALL,"listen %d(%s)\n",errno
,strerror(errno));
break;
}else{
logdebug (DBGALL,"bind ok\n");
break;
}
close (listen_handle);
listen_handle = -1;
if (i < 5) sleep (i*5);
}
}
}
/*
Ouverture d'un socket en mode listen pour serveur.
*/
PUBLIC CMDSOCK::CMDSOCK(const char *portname)
{
init (cmdsock_getport(portname));
}
/*
Ouverture d'un socket en mode listen pour serveur.
*/
PUBLIC CMDSOCK::CMDSOCK(int port)
{
if (port == -1){
/* #Specification: CMDSOCK / via inetd
A server using CMDSOCK can be started with inetd.
We specify -1 as the port number
CMDSOCK will then use the handle 0 to wait for
connections.
*/
listen_handle = 0;
}else{
init (port);
}
}
PUBLIC CMDSOCK::~CMDSOCK()
{
if (listen_handle != -1){
SOCK_INFO *pt = inf;
for (int i=0; i<nbcli; i++,pt++) close (pt->handle);
close (listen_handle);
}
}
/*
Retourne != 0 si le serveur est correctement initialise
*/
PUBLIC int CMDSOCK::is_ok()
{
return listen_handle != -1;
}
/*
Ferme la connexion d'un client.
*/
PUBLIC void CMDSOCK::closecli(int fd)
{
close (fd);
int dst = 0;
SOCK_INFO *pt = inf;
for (int i=0; i<nbcli; i++, pt++){
if (pt->handle != fd){
inf[dst++] = inf[i];
}
}
nbcli = dst;
}
/*
Ajoutte un handle de plus au serveur avec gestion de timeout.
Le timeout est en secondes.
La connexion de ce handle a ΘtΘ realisΘe par l'appelant. On
retrouve cette situation lorsqu'un serveur dΘmarre lui-mΩme
les clients ou entre en contact avec un autre serveur.
*/
PUBLIC void CMDSOCK::addcli (int fd, int timeout)
{
SOCK_INFO *pt = inf + nbcli++;
pt->handle = fd;
pt->idle.set = timeout * 1000000;
pt->idle.cur = 0;
pt->actif = 0;
}
/*
Ajoutte un handle de plus au serveur.
*/
PUBLIC void CMDSOCK::addcli (int fd)
{
addcli (fd,0);
}
/*
Attend de l'activite sur un handle parmi plusieurs ou une connection
sur un socket.
Return -1 if any error.
Return 0 if the timeout was met.
Return > 0 if something is available (or simply a new connexion).
In that case, readnext() should be called to get some work.
*/
PUBLIC int CMDSOCK::listen (
long timeout) // Microseconds
// -1 = no timeout
// 0 = pollmode
{
fd_set set;
FD_ZERO(&set);
int maxhd = 0;
SOCK_INFO *pt = inf;
for (int i=0; i<nbcli; i++,pt++){
int handle = pt->handle;
pt->actif = 0;
FD_SET (handle,&set);
if (handle > maxhd) maxhd = handle;
}
FD_SET (listen_handle,&set);
if (listen_handle > maxhd) maxhd = listen_handle;
struct timeval timeo;
if (timeout > 1000000){
timeo.tv_sec = timeout/1000000;
timeo.tv_usec = timeout%1000000;
}else{
timeo.tv_sec = 0;
timeo.tv_usec = timeout;
}
fd_set spcset;
spcset = set;
struct timeval *pttimeo = NULL;
if (timeout != -1) pttimeo = & timeo;
int sel = select (maxhd+1,&set,NULL,&spcset,pttimeo);
actif = 0;
int ret = 0;
if (sel > 0){
if (FD_ISSET(listen_handle,&set) != 0){
char sacc[100];
int size=100;
int fd = accept (listen_handle,(struct sockaddr *)sacc
,&size);
addcli (fd);
}
pt = inf;
for (i=0; i<nbcli; i++,pt++){
int handle = pt->handle;
if (FD_ISSET(handle,&spcset)!=0){
logdebug (DBGALL,"client %d dans spcset\n",handle);
}
if (FD_ISSET(handle,&set)){
pt->actif = 1;
pt->idle.cur = 0;
}else{
pt->idle.cur += timeout;
}
}
ret = 1;
}else{
// Aucune connexion active, on augmente les timeout
pt = inf;
for (i=0; i<nbcli; i++,pt++){
pt->idle.cur += timeout;
}
}
return ret;
}
/*
Retourne le nombre de connexion courramment monitorΘ.
Cette fonction est gΘnΘralement utilisΘe pour dΘcider si on doit
terminer le service
*/
PUBLIC int CMDSOCK::getnbcli()
{
return nbcli;
}
/*
Lit une transaction du prochain client actif.
Retourne 0 si le client a fermΘ la connexion.
Retourne -1 s'il n'y en a plus.
Retourne le nombre de byte placΘ dans buf. fd contiendra
le handle du client qui sera utilisΘ pour transmettre
la reponse.
*/
PUBLIC int CMDSOCK::readnext(void *buf, int size, int &cli)
{
int ret = -1;
SOCK_INFO *pt = inf + actif;
for ( ; actif < nbcli; actif++, pt++){
if (pt->actif){
cli = pt->handle;
logdebug (DBGALL,"Transaction du client %d\n",cli);
int nb = read (cli,buf,size);
if (nb > 0){
ret = nb;
actif++;
}else{
logdebug (DBGALL,"Client %d a ferme la connexion\n",cli);
closecli (cli);
ret = 0;
}
break;
}else if (pt->idle.set > 0){
if (pt->idle.cur > pt->idle.set){
cli = pt->handle;
ret = 0;
closecli (cli);
syslog (LOG_INFO,"Stale connexion %d, closing",cli);
break;
}
}
}
return ret;
}
#if 0
/*
Etablie la connexion avec un serveur.
Retourne -1 si erreur.
*/
int cmdsock_connect (const char *servname, int port, int nbretry)
{
int ret = -1;
struct hostent *h = (struct hostent *) gethostbyname(servname);
if (h == NULL){
logevent ("Pas de serveur \"%s\" dΘfini",servname);
}else{
struct sockaddr_in sin;
sin.sin_family = h->h_addrtype;
memcpy (&sin.sin_addr,h->h_addr, h->h_length);
sin.sin_port = htons(port);
for (int i=0; i<nbretry; i++){
int s;
if ((s = socket (AF_INET, SOCK_STREAM, 0)) >= 0) {
logdebug (DBGALL,"avantconnect %d\n",s);
if (connect (s, (struct sockaddr *) &sin, sizeof (sin))
== -1){
if (i==0){
logdebug (DBGALL,"Ne peut realiser connect (%s)\n"
,strerror(errno));
}
close (s);
sleep(1);
}else{
ret = s;
break;
}
}else{
logdebug (DBGALL,"socket");
}
}
}
return ret;
}
/*
Etablie la connexion avec un serveur.
Retourne -1 si erreur.
*/
int cmdsock_connect (const char *servname, const char *portname, int nbretry)
{
int port = cmdsock_getport(portname);
int ret = -1;
if (port != -1){
ret = cmdsock_connect (servname,port,nbretry);
}
return ret;
}
/*
Etablie la connexion avec le serveur.
Retourne -1 si erreur.
*/
int cmdsock_connect ()
{
/* #Specification: serveur / location
Les clients du serveur assume qu'il execute toujours
sur ngdhost. Il s'agit donc de mettre le bon alias dans
/etc/hosts ou ailleurs.
*/
/* #Specification: serveur / port de communication
La communication se fait par socket. Le port de communication
est controlΘ par /etc/service. On y retrouvera la ligne
suivante
ngd xxx/tcp
xxx sera un numΘro de port non utilisΘ < 1024.
*/
return cmdsock_connect ("ngdhost","ngd",10);
}
/*
Envoie un seul message via un socket. Le socket est Θtablie
α chaque transaction.
Retourne -1 si erreur.
*/
int cmdsock_sendmsg (char *retbuf, int size, const char *ctl, ...)
{
int ret = -1;
int fd = cmdsock_connect();
if (fd != -1){
va_list list;
va_start (list,ctl);
char buf[1000];
int nb = vsprintf (buf,ctl,list);
buf[nb] = '\0';
write (fd,buf,nb);
int readnb = read (fd,retbuf,size);
if (readnb > 0){
retbuf[readnb] = '\0';
ret = 0;
}
close (fd);
}
return ret;
}
/*
Retourne 1 s'il y a quelques chose a lire sur fd.
Retourne 0 si le timeout est ΘcoulΘ.
Retourne -1 s'il y a un erreur.
*/
int cmdsock_wait (int fd, long timeout)
{
int tb[1];
tb[0] = fd;
return cmdsock_wait (1,tb,tb,timeout);
}
/*
Retourne le nombre de handle prΩt pour une lecture (data disponible)
Retourne 0 si le timeout est ΘcoulΘ.
Retourne -1 s'il y a un erreur.
*/
int cmdsock_wait (int nbfds, int fds[],int ready[], long timeout)
{
fd_set set;
FD_ZERO (&set);
int maxfd = 0;
int i;
for (i=0; i<nbfds; i++){
int fd = fds[i];
FD_SET (fd,&set);
if (fd > maxfd) maxfd = fd;
}
struct timeval timeo;
if (timeout > 1000000){
timeo.tv_sec = timeout/1000000;
timeo.tv_usec = timeout%1000000;
}else{
timeo.tv_sec = 0;
timeo.tv_usec = timeout;
}
int ret = select (maxfd+1,&set,NULL,NULL,timeout == -1 ? NULL : &timeo);
if (ret > 0){
ret = 0;
for (i=0; i<nbfds; i++){
int fd = fds[i];
if (FD_ISSET (fd,&set)) ready[ret++] = fd;
}
}
return ret;
}
#endif
#ifdef TEST
static void usage_test()
{
fprintf (stderr,
"x serv ou x client\n"
"Le serveur retourne chaque ligne que le client envoie\n"
"avec un petit ajout\n"
);
}
int main (int argc, char *argv[])
{
if (argc == 2){
if (strncmp(argv[1],"ser",3)==0){
CMDSOCK cmd;
if (cmd.is_ok()){
while (1){
if (cmd.listen (3000000) != -1){
char buf[1000];
int nb;
int cli;
while ((nb=cmd.readnext(buf,sizeof(buf),cli))!=-1){
if (nb == 0){
printf ("cli %d a ferme la connexion\n",cli);
}else{
char repond[2000];
buf[nb] = '\0';
printf ("recoit requete :%s:\n",buf);
nb = sprintf (repond,"RECU ceci <%s>",buf);
write (cli,repond,nb);
}
}
}
}
}
}else if (strncmp(argv[1],"cli",3)==0){
while (1){
char buf[200];
printf ("Entrer n'importe quoi "); fflush (stdout);
fgets(buf,sizeof(buf)-1,stdin);
char retbuf[200];
if (cmdsock_sendmsg (retbuf,sizeof(retbuf),"%s",buf)==-1){
fprintf (stderr,"Ne peut transmettre au serveur\n");
}else{
printf ("Recoit :%s:\n",retbuf);
}
}
}else{
usage_test();
}
}else{
usage_test();
}
return 0;
}
#endif